package com.rengwuxian.coursecoroutines._2_structured_concurrency

import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.job
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import kotlin.coroutines.EmptyCoroutineContext

/**
 * SupervisorJob
 * Job/SupervisorJob、协程的取消、协程的异常
 *
 * 一句话：能取消子协程，但是不会被子协程的异常而取消。
 * SupervisorJob：担任主管功能的job
 * SupervisorJob和自己的子协程，只在取消流程是有效的，在异常流程是无效的。
 *     -supervisorJob在取消时，里面的子协程都会被取消
 *     -里面的子协程异常时，supervisorJob不会被连带取消
 *
 * 源码：
 * private class SupervisorJobImpl(parent: Job?) : JobImpl(parent) {
 *     override fun childCancelled(cause: Throwable): Boolean = false
 * }
 * childCancelled，但子协程被取消，父协程就会调用这个函数。
 *
 * 正常的Job原实现：
 *     public open fun childCancelled(cause: Throwable): Boolean {
 *         if (cause is CancellationException) return true
 *         return cancelImpl(cause) && handlesException
 *     }
 * 如果子协程是取消异常，则返回true，如果是正常异常，则取消自己。
 *
 * launch(SupervisorJob(coroutineContext.job) + handler)
 * 这种写法是非常常见的一种格式，没有切断父子协程的链条，而仍然保留着半父子关系。
 * 外面的协程取消，则里面的协程跟着取消，但是里面的协程抛异常，但不会取消外面的协程。
 * CoroutineScope(SupervisorJob())，这种写法也很常见，理由是同上的。
 *
 * SupervisorJob() 对内开启的子协程，这个子协程本身就是最顶层的协程了，抛异常会抛到这为止，然后传递给线程世界，不会再抛到SupervisorJob()的父协程了。
 *
 * 下面代码输出：
 * Caught in handler: java.lang.RuntimeException: Error!
 * Parent Job cancelled: false
 */
fun main() = runBlocking<Unit> {
  val scope = CoroutineScope(SupervisorJob())
  val supervisorJob = SupervisorJob()
  val job = Job()
  scope.launch(job) {
    val handler = CoroutineExceptionHandler { _, exception ->
      println("Caught in handler: $exception")
    }
    launch(SupervisorJob(coroutineContext.job) + handler) {// 这种写法：SupervisorJob作为外面协程的子协程，不会切断父子关系。
      launch {
        throw RuntimeException("Error!")
      }
    }
  }
  delay(1000)
  println("Parent Job cancelled: ${job.isCancelled}")
  delay(10000)
}